import json
import random
import uuid
from dataclasses import dataclass
from datetime import datetime, timedelta
from operator import itemgetter
import re
from typing import Any, Type

from flask import session
from injector import inject
from langchain_core.messages import AIMessage
from langchain_core.runnables import RunnableConfig, RunnablePassthrough, RunnableLambda
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from sqlalchemy import func, desc, update, asc, distinct, extract, cast, Integer, case

from src.pkg.sqlalchemy import SQLAlchemy
from .base_service import BaseService
from ..exception import NotFoundException, FailException
from ..model import Exercise, StudentAnswer, Category, LearningProgress, ExerciseUser, ExerciseUserMans, \
    StudentUserAnswer, LearningUserProgress
from src.pkg.response import success_json, success_message
from ..model.category_user import CategoryUser
from ..model.exercises_main_model import ExerciseMans
from ..pkg.pageinator import Pageinator, PaginatorReq
from langchain_core.tools import BaseTool
from werkzeug.datastructures import FileStorage
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
import os
class ArgsCategorys(BaseModel):
    arr: list[str] = Field(description="The arr field represents the key knowledge field, dividing each knowledge point with a newline")


class ArgsToolsItem(BaseModel):
    question: str = Field(description="The generated question")
    answer: str = Field(description="The generated answer")
    scores: int = Field(
        description="Generate scores for 10 questions, where each question has a unique score. The total score of all questions should add up to exactly 100 points, with scores distributed as variably as possible.")
    answer_result: str = Field(
        description="The answer_result field stores the final answer, not the process, but just the final result, and does not generate anything other than the result field, which cannot be empty")


class ArgsTools(BaseModel):
    arr: list[ArgsToolsItem] = Field(description="Is the parent of question and answer")


class ArgsAnswer(BaseModel):
    solution_idea: str = Field(description="The solution_idea field indicates the solution idea field")
    detailed_proof: list[str] = Field(
        description="The detailed_proof field represents the detailed proof field, with a newline separating the points of each proof")
    key_knowledge: list[str] = Field(
        description="The key_knowledge field represents the key knowledge field, dividing each knowledge point with a newline")
    is_correct: str = Field(
        description="The is_correct field indicates error or correct, where the correct is 1 and the wrong is 0")


class JsonTools(BaseTool):
    """
    You are an intelligent assistant that can generate json format
    """
    name: str = "json_tool"
    description: str = "You are an intelligent assistant that can generate json format"
    args_schema: Type[BaseModel] = ArgsTools

    def _run(self, *args: Any, **kwargs: Any) -> Any:
        arrs = kwargs.get("arr")
        return [{"question": item.question, "answer": item.answer, "scores": item.scores,
                 "answer_result": item.answer_result} for item in arrs]


class CateGoryTools(BaseTool):
    name: str = "cate_gorys"
    description: str = "A tool that generates three key knowledge points by name"
    args_schema: Type[BaseModel] = ArgsCategorys

    def _run(self, *args: Any, **kwargs: Any) -> Any:
        arr = kwargs.get("arr")
        return arr


class JsonAnswerTools(BaseTool):
    """
    You are an intelligent assistant that can generate json format
    """
    name: str = "json_answer_tools"
    description: str = "You are an intelligent assistant that can generate json format"
    args_schema: Type[BaseModel] = ArgsAnswer

    def _run(self, *args: Any, **kwargs: Any) -> Any:
        solution_idea = kwargs.get("solution_idea")
        detailed_proof = kwargs.get("detailed_proof")
        key_knowledge = kwargs.get("key_knowledge")
        is_correct = kwargs.get("is_correct")
        return {
            "solution_idea": solution_idea,
            "detailed_proof": detailed_proof,
            "key_knowledge": key_knowledge,
            "is_correct": is_correct,
        }


class ExercisesBean(BaseModel):
    arr: list[dict] = Field(
        description="Use question fields to store AI generated questions, use answer fields to store AI generated answers, and require storage in Japanese")


class AnswersBean(BaseModel):
    data: dict = Field(
        description="The solution_idea field indicates the solution idea field, the detailed_proof field indicates the detailed proof field, the key_knowledge field indicates the key knowledge field, and the is_correct field indicates that all fields are correct")


@inject
@dataclass
class ExercisesService(BaseService):
    db: SQLAlchemy

    # 难度系数：1（容易）2（中等）3（困难）。
    # 请根据以下难度系数和分类进行回答
    # 难度系数：{num}。
    # 题目分类: {type}
    def __init__(self, db: SQLAlchemy):
        self.tag = ""
        self.db = db
        self.llm = ChatOpenAI(model="gpt-4o-2024-08-06", temperature=0)
        self.user_category_exercises = """
        You are an expert at generating exercises based on scores and categories. You will generate {nums} exercises at a time, and the difficulty of generating exercises will be determined by the score. The scoring line is as follows:
        1. The passing mark is 50 points
        2. Proficiency of 70 or above
        3. A score below 50 is considered a failing grade
        Each generated question and answer cannot be repeated, and the total generated score for all questions must be 100 points to ensure randomness and generation conform to utf-8 and not encoded by gbk related format characters
        Topic Type:
        Geometry: involves geometric concepts such as shape, space, Angle, area, volume, etc.
        Algebra: including equations, inequalities, functions, series, matrices and other algebraic knowledge.
        Calculus: Covers the basics of calculus, such as derivatives, integrals, and limits.
        Scores submitted by users: {source}
        Category submitted by user: {category_name}
        Agent requirements:
        The topic content should match the specified topic type.
        The questions generated should be representative and able to cover the main knowledge points under the type.
        Note: The output cannot be empty, must meet all the above conditions, be unbiased, and the output must be in Japanese.
        To further ensure that the generated questions are not duplicated, we can consider adding a random seed to the generated questions so that the generated questions are different each time. At the same time, we can also check the existing problem library before generating the problem to ensure that the newly generated problem does not duplicate the existing problem.
        In addition, in order to ensure the number of problems required for generation, we can explicitly specify the number of problems to be generated ({nums}) before the generation of problems, and count during the generation process to ensure that the number of problems generated is accurate.
        Note: The output cannot be empty, must meet all the above conditions, be unbiased, and the output must be in Japanese.
        """
        self.exercises_template = """
       You are an assistant who can generate non-repetitive answers and questions.
        Generate {exercises_num} high school math problems based on the criteria and type specified, depending on the type of problem requested by the user. Each generated question and answer cannot be repeated, and the total number of generated scores for all questions must meet 100 points to ensure randomness and generation conform to utf-8, rather than encoded by gbk related format characters
        Topic Type:
        Geometry: involves geometric concepts such as shape, space, Angle, area, volume, etc.
        Algebra: including equations, inequalities, functions, series, matrices and other algebraic knowledge.
        Calculus: Covers the basics of calculus, such as derivatives, integrals, and limits.
        User question type: {type}
        Agent requirements:
        The topic content should match the specified topic type.
        The questions generated should be representative and able to cover the main knowledge points under the type.
        Note: The output content must not be empty, must meet all the above conditions, no bias, and the output must be in Japanese.
        To further ensure that the generated questions are not duplicated, we can consider adding a random seed to the generated questions so that the generated questions are different each time. At the same time, we can also check the existing problem library before generating the problem to ensure that the newly generated problem does not duplicate the existing problem.
        Also, to ensure that the required number of issues is generated, we can explicitly specify the number of issues to be generated ({exercises_num}) before the issues are generated and count them during the generation to ensure that the number of issues generated is accurate.
        Note: The output content must not be empty, must meet all the above conditions, no bias, and the output must be in Japanese.
        """
        self.answer_prompts = """
         You are an expert in question and answer determination and can perform batch tests based on user-supplied high school math questions and answers.
        According to the questions submitted by the user, the answers answered by the user and the answers generated by the ai, please make a judgment and give the solution ideas, as well as detailed proof and key knowledge points, please output in Japanese.
        You must also give a correct or incorrect answer, 0 (false) 1 (true)
        You want to reasoning whether the answer submitted by the user matches the correct answer, a user's answer and the correct answer do not have to match the text exactly the same, the meaning is the same
        You need to give three key points.
        User question: {question}
        Correct answer :{answer}
        User answer: {user_answer}
        Please refer to the following examples in a format that meets the requirements of the following examples:
        Problem idea: According to the conditions of the problem, DE∥BC can be proved by the decision theorem of similar triangles.  The main method is to use the interior point theorem and parallel lines.
        Detailed proof:
        1.  If AD:DB=2:3, then D is the internal component of AB, AD:AB=2:5
        2.  Similarly,AE: AC = 2:5
        3.  According to the inner equinox theorem, D and E are equal fractions of the corresponding edges
        4.  According to the theorem of determining parallel lines, DE∥BC can be obtained
        Key knowledge points:
        1.  Similar triangle theorem
        2.  The inner equinox theorem
        3.  Parallel line properties
        """
        self.prompt_sim = """
        You are an expert at generating three key knowledge points based on questions. Here are the user questions
        question:{query}
         Here is an example(For reference only)：
         1. 数と式
         2. 図形と計量
         3. データの分析
          """

    def get_question(self, category_id: int, branch_id: str):
        data = self.db.session.query(Exercise).filter_by(compile=-1, user_id=session["user_id"],
                                                         category_id=category_id, branch=branch_id).order_by(
            asc("exercise_id")).first()
        print(data)
        start_time = datetime.utcnow()
        learningProgress = self.db.session.query(LearningProgress).filter(
            LearningProgress.exercise_id == data.exercise_id,
            LearningProgress.user_id == session["user_id"]).one_or_none()
        if learningProgress is None:
            self.create(
                LearningProgress,
                exercise_id=data.exercise_id,
                user_id=session["user_id"],
                branch_id=branch_id,
                start_time=start_time,
                duration=None
            )
        else:
            self.update(
                learningProgress,
                start_time=start_time,
                end_time=None,
                duration=None
            )
        return data
    def get_user_question(self, category_id: int, branch_id: str):
        data = self.db.session.query(ExerciseUser).filter_by(compile=-1, user_id=session["user_id"],
                                                         category_id=category_id, branch=branch_id).order_by(asc("exercise_id")).first()
        print("哈哈哈哈哈哈哈")
        print(data)

        start_time = datetime.utcnow()
        learningUserProgress = self.db.session.query(LearningUserProgress).filter(
            LearningUserProgress.exercise_id == data.exercise_id,
            LearningUserProgress.user_id == session["user_id"]).one_or_none()
        if learningUserProgress is None:
            self.create(
                LearningUserProgress,
                exercise_id=data.exercise_id,
                user_id=session["user_id"],
                branch_id=branch_id,
                start_time=start_time,
                duration=None
            )
        else:
            self.update(
                learningUserProgress,
                start_time=start_time,
                end_time=None,
                duration=None
            )


        return data

    def get_category_scores(self, req,start_date=None, end_date=None):
        pageinator = Pageinator(self.db, req)
        filter = []
        if start_date is not None or start_date:
            filter.append(StudentAnswer.created_at>=start_date)
            filter.append(StudentAnswer.created_at<end_date)
            filter.append(StudentAnswer.user_id == session["user_id"])
        data_list = pageinator.paginate(
            self.db.session.query(StudentAnswer).filter(*filter).order_by(desc("updated_at"))
        )

        return data_list,pageinator


    def get_question_sign(self, exercise_id: int):
        data = self.db.session.query(Exercise).filter_by(user_id=session["user_id"],
                                                         exercise_id=exercise_id).first()

        start_time = datetime.utcnow()
        learningProgress = self.db.session.query(LearningProgress).filter(
            LearningProgress.exercise_id == data.exercise_id,
            LearningProgress.user_id == session["user_id"]).one_or_none()
        self.update(
            learningProgress,
            end_time=None,
            start_time=start_time,
            duration=None
        )

        return data

    def get_question_user_sign(self, exercise_id: int):

        data = self.db.session.query(ExerciseUser).filter_by(user_id=session["user_id"],
                                                         exercise_id=exercise_id).first()
        start_time = datetime.utcnow()
        learningProgress = self.db.session.query(LearningUserProgress).filter(
            LearningUserProgress.exercise_id == data.exercise_id,
            LearningUserProgress.user_id == session["user_id"]).one_or_none()
        self.update(
            learningProgress,
            end_time=None,
            start_time=start_time,
            duration=None
        )
        return data

    def try_gen(self, exercise_id: int, category_id: int):
        return self.select_sub(1, category_id, exercise_id)
    def try_user_gen(self, exercise_id: int, category_id: int,source,category_name):
        return self.select_sub(1, category_id, exercise_id,source,category_name)
    def user_category(self,source:int,category_id:int)->str: #生成个性化推荐练习题
        category = self.db.session.query(Category).filter(Category.id==category_id).one_or_none()
        category_data = self.db.session.query(CategoryUser).filter(CategoryUser.user_id == session["user_id"],CategoryUser.name==category.name).one_or_none()
        if category_data is None:
            model: CategoryUser = self.create(
            CategoryUser,
            name=category.name,
            source=source,
            user_id=session["user_id"],
            category_name=f"{session['username']}-{len(self.db.session.query(CategoryUser).all())+1}"
            )
            self.select_sub(5,model.id,None,source,category.name)
        else:
            d = self.db.session.query(ExerciseUser).filter(ExerciseUser.user_id == session["user_id"],ExerciseUser.category_id==category_data.id).all()
            self.select_sub(5, category_data.id, None, source, category.name)

    def select_sub(self, exercises_num: int, category_id: int, exercise_id=None,source=None,category_name=None):
        prompt=None
        chain=None
        data=None
        categoryData=None
        nums = 0
        frequency_penaltys = None
        seed = 0
        if exercises_num == 10 or exercises_num ==5:
            nums = 0
        elif exercises_num == 1:
            seed = nums
            nums = random.uniform(0, 0.5)
            frequency_penaltys = 2.0
        self.llm = ChatOpenAI(model="gpt-4o-2024-08-06", temperature=nums, frequency_penalty=frequency_penaltys
                           ).bind_tools([JsonTools()], strict=True)


        if source is  None or category_name is  None and exercise_id is None:
            categoryData = self.db.session.query(Category).filter_by(id=category_id).first()
            if categoryData is None:
                raise NotFoundException("该科目不存在")
            prompt = ChatPromptTemplate.from_template(self.exercises_template)
            chain = ({
                         "type": itemgetter("type"),
                         "exercises_num": itemgetter("exercises_num")

                     } | prompt | self.llm | (lambda x: x.tool_calls[0]["args"]) | JsonTools())
            data = chain.invoke({"type": categoryData.name, "exercises_num": exercises_num})
        else:
            categoryData = self.db.session.query(CategoryUser).filter_by(id=category_id).first()
            if categoryData is None:
                raise NotFoundException("该科目不存在")

            prompt = ChatPromptTemplate.from_template(self.user_category_exercises)
            chain = ({
                         "source": itemgetter("source"),
                          "nums":itemgetter("nums"),
                         "category_name": itemgetter("category_name"),
                     } | prompt  | self.llm | (lambda x: x.tool_calls[0]["args"]) | JsonTools())
            data = chain.invoke({"source": source, "category_name": category_name,"nums":exercises_num})

        if exercise_id is None and source is None:
            uid = str(uuid.uuid4())
            model: ExerciseMans = self.create(
                ExerciseMans,
                branch_id=uid,
                user_id=session["user_id"],
                name=categoryData.name,
                compile=-1,
                category_id=category_id,
            )
            for dataArr in data:
                """存储习题"""
                self.create(Exercise,
                            subject="数学",
                            branch_id=uid,
                            branch=uid,
                            similarit_category=[],
                            exercises_main_id=model.id,
                            compile=-1,
                            answer_result=json.dumps(dataArr["answer_result"]),
                            score=dataArr["scores"],
                            category_id=category_id,
                            user_id=session["user_id"],
                            question_text=json.dumps(dataArr["question"]),
                            answer_text=json.dumps(dataArr["answer"]),
                            )

            return uid
        elif source is not None:
            print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
            d = self.db.session.query(ExerciseUser).filter_by(exercise_id=exercise_id,user_id=session["user_id"]).one_or_none()
            uid = str(uuid.uuid4())
            model: ExerciseUserMans = self.create(
                ExerciseUserMans,
                branch_id=uid,
                user_id=session["user_id"],
                name=categoryData.name,
                compile=-1,
                category_id=category_id,
            )
            print(data[0]["question"])
            print("zzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
            if d is not None or d:
                        self.update(
                            d,
                            question_text=json.dumps(data[0]["question"]),
                            answer_result=json.dumps(data[0]["answer_result"]),
                            answer_text=json.dumps(data[0]["answer"])
                        )
            else:
                for dataArr in data:
                    """存储习题"""
                    self.create(ExerciseUser,
                                subject="数学",
                                branch_id=uid,
                                branch=uid,
                                similarit_category=[],
                                exercises_main_id=model.id,
                                compile=-1,
                                answer_result=json.dumps(dataArr["answer_result"]),
                                score=dataArr["scores"],
                                category_id=category_id,
                                user_id=session["user_id"],
                                question_text=json.dumps(dataArr["question"]),
                                answer_text=json.dumps(dataArr["answer"]),
                                )
        else:
            d = self.db.session.query(Exercise).filter_by(exercise_id=exercise_id,
                                                          user_id=session["user_id"]).one_or_none()

            self.update(d, question_text=json.dumps(data[0]["question"]),
                        answer_result=json.dumps(data[0]["answer_result"]), answer_text=json.dumps(data[0]["answer"]))
            return d.branch_id

    def exit(self, branch_id: str, exercise_id: int):
        exercise = self.db.session.query(Exercise).filter_by(user_id=session["user_id"], branch_id=branch_id,
                                                             exercise_id=exercise_id).all()
        if len(exercise) == 0:
            raise NotFoundException("该习题不存在")
        return self.update(
            exercise,
            compile=-1
        )
    def error_list(self,req:PaginatorReq,status:str,category_id:Any,exercises_name:str)-> tuple[list[ExerciseMans], Pageinator]:
        print(exercises_name)
        pageinator = Pageinator(self.db, req)
        filter = []
        if category_id is not None:
            filter.append(StudentAnswer.category_id==category_id)
        if  exercises_name or exercises_name is not None:
            filter.append(StudentAnswer.exercises_text.ilike(f"%{exercises_name}%"))
        print(*filter)
        if status == "today":
            # 获取当前日期
            today = datetime.today()
            # 获取今天的开始时间（00:00:00）
            start_of_day = today.replace(hour=0, minute=0, second=0, microsecond=0)
            # 获取今天的结束时间（23:59:59）
            end_of_day = today.replace(hour=23, minute=59, second=59, microsecond=999999)
            data_list = pageinator.paginate(
                self.db.session.query(StudentAnswer).filter(*filter,StudentAnswer.user_id == session["user_id"],StudentAnswer.updated_at>=start_of_day,StudentAnswer.updated_at<end_of_day,
                                                           StudentAnswer.is_correct==False).order_by(desc("updated_at")))

            return data_list, pageinator
        elif status =="all":
            data_list = pageinator.paginate(
        self.db.session.query(StudentAnswer).filter(*filter,StudentAnswer.user_id == session["user_id"],
                                                           StudentAnswer.is_correct==False).order_by(desc("created_at")))
            return data_list, pageinator

    def add_all(self):
        arr: list = [
    {"name": "数と式"},
    {"name": "図形と計量"},
    {"name": "二次関数"},
    {"name": "データの分析"},
    {"name": "いるいるな式"},
    {"name": "図形と方程式"},
    {"name": "微分・積分の考元"},
    {"name": "極限"},
    {"name": "微分法"},
    {"name": "積分法"},
    {"name": "図形の性質"},
    {"name": "数学と人間の活動"},
    {"name": "数列"},
    {"name": "統計的な推測"},
    {"name": "数学と社会生活"},
    {"name": "ベクトル"},
    {"name": "平面上の曲線と複素数平面"},
    {"name": "数学的な表現の工夫"},
    ]



        catgorys = [Category(name=item["name"]) for item in arr]
        with self.db.auto_commit():
            self.db.session.add_all(catgorys)
        return "success"

    def sucees_list(self, req: PaginatorReq) -> tuple[list[ExerciseMans], Pageinator]:
        pageinator = Pageinator(self.db, req)
        data_list = pageinator.paginate(
            self.db.session.query(ExerciseMans).filter(ExerciseMans.user_id == session["user_id"],
                                                       ExerciseMans.compile == 1).order_by(desc("created_at")))
        return data_list, pageinator

    def sucees_user_list(self, req: PaginatorReq) -> tuple[list[ExerciseUserMans], Pageinator]:
        pageinator = Pageinator(self.db, req)
        data_list = pageinator.paginate(
            self.db.session.query(ExerciseUserMans).filter(ExerciseUserMans.user_id == session["user_id"],
                                                       ExerciseUserMans.compile == 1).order_by(desc("created_at")))
        return data_list, pageinator

    def audio_uploads(self,file:FileStorage,exercise_id):
        auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
        bucket = oss2.Bucket(auth, os.getenv("ENDPOINT"), os.getenv("BUCKET_NAME"), region=os.getenv("REGION"))
        file_name = f"{exercise_id}.mp3"
        f = file.stream.read()
        upload_file_name = f"audio/{file_name}"
        bucket.put_object(upload_file_name, f)
        return f"{os.getenv('BUCKET_URL')}/{upload_file_name}"
    def chat_audio_upload(self,file:FileStorage):
        auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
        bucket = oss2.Bucket(auth, os.getenv("ENDPOINT"), os.getenv("BUCKET_NAME"), region=os.getenv("REGION"))
        file_name = file.filename
        extension = file_name.rsplit(".", 1)[-1] if "." in file_name else ""
        f = file.stream.read()
        new_file_name = f"{uuid.uuid4()}.{extension}"
        now = datetime.now()
        upload_file_name = f"{now.year}/{now.month:02d}/{now.day:02d}/audio/{new_file_name}"
        bucket.put_object(upload_file_name, f)
        return f"{os.getenv('BUCKET_URL')}/{upload_file_name}"

    def get_audio_url(self,exercise_id:int) -> str:
        upload_file_name = f"audio/{exercise_id}.mp3"
        return f"{os.getenv('BUCKET_URL')}/{upload_file_name}"
    def getCategoryList(self):
        return self.db.session.query(Category).all()

    def getCateGory(self, ids: list[int]):
        return self.db.session.query(Category).filter(Category.id.in_(ids)).all()

    def create_category(self, question: str):
        tools_llm =  ChatOpenAI(model="gpt-4o-2024-08-06",temperature=0).bind_tools([CateGoryTools()], strict=True)
        p_sim = ChatPromptTemplate.from_template(self.prompt_sim)
        chain_sim = ({
                         "query": RunnablePassthrough(),
                     } | p_sim | tools_llm | (lambda x: x.tool_calls[0]["args"]) | CateGoryTools())
        data = chain_sim.invoke({"query":question})
        return data
        # max_id_query = self.db.session.query(func.max(Category.id)).scalar()
        # if max_id_query is None:
        #     max_id = 0  # 如果表为空，则从 0 开始
        # else:
        #     max_id = max_id_query
        # categorys = [
        #     Category(name=item, id=max_id + i + 1)
        #     for i, item in enumerate(data, start=0)
        #     if item.strip()
        # ]
        # with self.db.auto_commit():
        #     self.db.session.add_all(categorys)
        # keys = [obj.id for obj in categorys]

    def get_week_success_task(self):
        # 统计本周
        today = datetime.now()
        weekday_today = today.weekday()
        start_date = today - timedelta(days=weekday_today)  # 周一零点
        start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_date = start_date + timedelta(days=7)  # 周五的午夜
        print(start_date)
        records = self.db.session.query(StudentAnswer).filter(
            StudentAnswer.user_id == session["user_id"],
            StudentAnswer.updated_at >= start_date,
            StudentAnswer.updated_at < end_date
        ).all()
        ex_mans = self.db.session.query(Exercise).filter(Exercise.user_id == session["user_id"], Exercise.branch_id.in_(
            list(set(ids.branch_id for ids in records)))).all()

        process = self.db.session.query(LearningProgress).filter(LearningProgress.user_id == session["user_id"],
                                                                 LearningProgress.start_time >= start_date,
                                                                 LearningProgress.start_time < end_date).all()
        sucees_total = len(ex_mans)
        print(ex_mans)
        correct_tasks = sum(1 for record in records if record.is_correct == True)  # 本周答对的题目数
        completed_tasks = sum(1 for ex_man in ex_mans if ex_man.compile == 1)  # 本周完成的题目数
        total_study_time = sum(proces.duration for proces in process if proces.duration)  #本周做题时长
        print(completed_tasks)
        print(sucees_total)
        # 计算完成率和正确率
        completion_rate = (completed_tasks / sucees_total) * 100 if sucees_total else 0  #本周完成率

        correctness_rate = (correct_tasks / sucees_total) * 100 if sucees_total else 0  #本周做题正确率
        print(records)
        return {
            "correct_tasks": correct_tasks,
            "completed_tasks": len(records),
            "completion_rate": round(completion_rate,0),
            "correctness_rate": round(correctness_rate,0),
            "total_study_time": total_study_time
        }


    def get_week_user_success_task(self):
        # 统计本周
        today = datetime.now()
        weekday_today = today.weekday()
        start_date = today - timedelta(days=weekday_today)  # 周一零点
        start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_date = start_date + timedelta(days=7)  # 周五的午夜
        print(start_date)
        records = self.db.session.query(StudentUserAnswer).filter(
            StudentUserAnswer.user_id == session["user_id"],
            StudentUserAnswer.updated_at >= start_date,
            StudentUserAnswer.updated_at < end_date
        ).all()
        ex_mans = self.db.session.query(ExerciseUser).filter(ExerciseUser.user_id == session["user_id"], ExerciseUser.branch_id.in_(
            list(set(ids.branch_id for ids in records)))).all()

        process = self.db.session.query(LearningUserProgress).filter(LearningUserProgress.user_id == session["user_id"],
                                                                 LearningUserProgress.start_time >= start_date,
                                                                 LearningUserProgress.start_time < end_date).all()
        sucees_total = len(ex_mans)
        print(ex_mans)
        correct_tasks = sum(1 for record in records if record.is_correct == True)  # 本周答对的题目数
        completed_tasks = sum(1 for ex_man in ex_mans if ex_man.compile == 1)  # 本周完成的题目数
        total_study_time = sum(proces.duration for proces in process if proces.duration)  #本周做题时长
        print(completed_tasks)
        print(sucees_total)
        # 计算完成率和正确率
        completion_rate = (completed_tasks / sucees_total) * 100 if sucees_total else 0  #本周完成率

        correctness_rate = (correct_tasks / sucees_total) * 100 if sucees_total else 0  #本周做题正确率
        print(records)
        return {
            "correct_tasks": correct_tasks,
            "completed_tasks": len(records),
            "completion_rate": round(completion_rate,0),
            "correctness_rate": round(correctness_rate,0),
            "total_study_time": total_study_time
        }



    def get_score(self, branch_id: str):
        return self.db.session.query(func.coalesce(func.sum(Exercise.score), 0)).filter(Exercise.branch_id == branch_id,
                                                                                        Exercise.user_id == session[
                                                                                            "user_id"],
                                                                                        Exercise.compile == 1).scalar()

    def end(self, branch_id: str):
        return self.db.session.query(StudentAnswer).filter(StudentAnswer.branch_id == branch_id,
                                                           StudentAnswer.user_id == session["user_id"],
                                                           StudentAnswer.is_correct == False).all()

    def get_moth_count(self):
        # 统计本月
        # 获取当前日期
        today = datetime.today()

        # 获取本月的第一天
        first_day_of_month = today.replace(day=1)

        # 获取下个月的第一天，然后减去一天得到本月的最后一天
        if today.month == 12:
            next_month = today.replace(year=today.year + 1, month=1, day=1)
        else:
            next_month = today.replace(month=today.month + 1, day=1)
        last_day_of_month = next_month - timedelta(days=1)
        completed_records = self.db.session.query(StudentAnswer).filter(
            StudentAnswer.user_id == session["user_id"],
            StudentAnswer.updated_at >= first_day_of_month,
            StudentAnswer.updated_at <= last_day_of_month
        ).all()
        return len(completed_records)

    def today(self):
        #统计今日
        # 获取当前日期
        today = datetime.today()
        # 获取今天的开始时间（00:00:00）
        start_of_day = today.replace(hour=0, minute=0, second=0, microsecond=0)
        # 获取今天的结束时间（23:59:59）
        end_of_day = today.replace(hour=23, minute=59, second=59, microsecond=999999)
        completed_today = self.db.session.query(StudentAnswer).filter(
            StudentAnswer.user_id == session["user_id"],
            StudentAnswer.updated_at >= start_of_day,
            StudentAnswer.updated_at <= end_of_day
        ).all()
        completed_today_count = len(completed_today)
        return completed_today_count

    def study_days(self):
        #累计学习天数
        # 获取所有已完成的记录
        completed_records = self.db.session.query(LearningProgress).filter(
            LearningProgress.user_id == session["user_id"]).all()

        # 提取每条记录的日期部分，并去重
        unique_days = set(record.created_ar.date() for record in completed_records)

        # 统计累计学习天数
        cumulative_learning_days = len(unique_days)
        return cumulative_learning_days

    def answer_sum(self):
        #解题数量
        count = self.db.session.query(func.count(StudentAnswer.answer_id)).filter(
            StudentAnswer.user_id == session["user_id"]).scalar()
        return count

    def sum_success_rate(self):
        #正确率统计
        records = self.db.session.query(StudentAnswer).filter(StudentAnswer.user_id == session["user_id"]).all()
        success_tasks = sum(1 for record in records if record.is_correct == True)
        records_sum = len(records)
        success_rate = (success_tasks / records_sum) * 100 if records_sum else 0
        return round(success_rate,0)

    def getTimeScore(self, branch_id: str) -> tuple[int, int]:
        score = self.db.session.query(func.coalesce(func.sum(StudentAnswer.score), 0)).filter(
            StudentAnswer.user_id == session["user_id"], StudentAnswer.branch_id == branch_id,
            StudentAnswer.is_correct == True).scalar()

        time = self.db.session.query(func.coalesce(func.sum(LearningProgress.duration), 0)).filter(
            LearningProgress.branch_id == branch_id, LearningProgress.user_id == session["user_id"]).scalar()
        return score, time


    def fil_list(self, req: PaginatorReq) -> tuple[list[ExerciseMans], Pageinator]:
        pageinator = Pageinator(self.db, req)
        data_list = pageinator.paginate(
            self.db.session.query(ExerciseMans).filter(ExerciseMans.user_id == session["user_id"],
                                                       ExerciseMans.compile == -1).order_by(desc("created_at")))
        return data_list, pageinator
    def del_fil_list(self,id:int):
        with self.db.auto_commit():
            student = self.db.session.query(ExerciseMans).filter(ExerciseMans.id == id).one_or_none()
            self.db.session.delete(student)

    def fil_user_list(self, req: PaginatorReq) -> tuple[list[ExerciseUserMans], Pageinator]:
        pageinator = Pageinator(self.db, req)
        data_list = pageinator.paginate(
            self.db.session.query(ExerciseUserMans).filter(ExerciseUserMans.user_id == session["user_id"],
                                                       ExerciseUserMans.compile == -1).order_by(desc("created_at")))
        return data_list, pageinator
    def del_fil_user_list(self,id:int):
        data = self.db.session.query(ExerciseUserMans).filter(ExerciseUserMans.user_id == session["user_id"],ExerciseUserMans.id==id).one_or_none()
        with self.db.auto_commit():
            self.db.session.delete(data)

    def error_replay(self, branch_id: str):
        data = self.db.session.query(StudentAnswer).filter(StudentAnswer.user_id == session["user_id"],
                                                           StudentAnswer.is_correct == False,
                                                           StudentAnswer.branch_id == branch_id).all()
        return data

    def error_user_replay(self, branch_id: str):
        data = self.db.session.query(StudentUserAnswer).filter(StudentUserAnswer.user_id == session["user_id"],
                                                           StudentUserAnswer.is_correct == False,
                                                           StudentUserAnswer.branch_id == branch_id).all()
        return data
    def getScoreSum(self):
        data = self.db.session.query(func.coalesce(func.sum(LearningProgress.duration),0)).filter(
            LearningProgress.user_id == session["user_id"]
        ).scalar()
        return round(data,0)

    def get_random_categories(self):
        # 查询5条随机数据
        random_categories = Category.query.group_by(Category.id).order_by(func.random()).limit(30).all()

        # 将结果转换为JSON格式
        categories_list = [{"id": category.id, "name": category.name} for category in random_categories]

        return success_json(categories_list)

    def success_count(self, branch_id: str):
        return self.db.session.query(func.count(StudentAnswer.answer_id)).filter(
            StudentAnswer.user_id == session["user_id"], StudentAnswer.branch_id == branch_id,
            StudentAnswer.is_correct == True).scalar()

    def time_view(self, branch_id: str):
        return self.db.session.query(func.sum(LearningProgress.duration)).filter(
            LearningProgress.user_id == session["user_id"], LearningProgress.branch_id == branch_id).scalar()

    """答题"""

    def answer(self, question: str, answer: str, user_answer: str,
               answer_id: int, exercise_id: int, category_id: int, branch_id: str):
        self.llm = ChatOpenAI(model="gpt-4o-2024-08-06", temperature=0).bind_tools([JsonAnswerTools()], strict=True)

        prompt = ChatPromptTemplate.from_template(self.answer_prompts)
        chain = ({
                     "question": itemgetter("question"),
                     "answer": itemgetter("answer"),
                     "user_answer": itemgetter("user_answer")
                 } | prompt | self.llm | (lambda x: x.tool_calls[0]["args"]) | JsonAnswerTools())

        data = chain.invoke({"question": question, "answer": answer, "user_answer": user_answer})
        exercise = self.db.session.query(Exercise).filter_by(exercise_id=exercise_id,
                                                             user_id=session["user_id"]).one_or_none()
        if exercise is None:
            raise NotFoundException("没有该习题")
        answer_emtry = self.db.session.query(StudentAnswer).filter_by(answer_id=answer_id,
                                                                      user_id=session["user_id"]).one_or_none()
        self.update(
            exercise,
            branch_id=exercise.branch_id,
            compile=1
        )
        record = self.db.session.query(LearningProgress).filter_by(user_id=session["user_id"], branch_id=branch_id,
                                                                   end_time=None,
                                                                   exercise_id=exercise.exercise_id).one_or_none()
        end_time = datetime.utcnow()

        duration = round((end_time - record.start_time).total_seconds() / 60, 2)  # 转换为小时
        self.update(
            record,
            end_time=end_time,
            duration=duration
        )
        if answer_emtry is None:
            answer_emtry = self.create(
                StudentAnswer,
                score=exercise.score,
                is_correct=True if data["is_correct"] == "1" else False,
                user_answer=json.dumps(user_answer),
                category_id=category_id,
                answer_result=exercise.answer_result,
                branch_id=branch_id,
                exercises_text=json.loads(exercise.question_text),
                detailed_proof=json.dumps(data["detailed_proof"]),
                solution_idea=json.dumps(data["solution_idea"]),
                key_knowledge=json.dumps(data["key_knowledge"]),
                exercise_id=exercise_id,
                user_id=session["user_id"]
            )
        else:
            self.update(
                answer_emtry,
                user_id=session["user_id"],
                answer_id=answer_id,
                is_correct=True if data["is_correct"] == "1" else False,
                user_answer=json.dumps(user_answer),
                exercises_text=json.loads(exercise.question_text),
                answer_result=exercise.answer_result,
                detailed_proof=json.dumps(data["detailed_proof"]),
                solution_idea=json.dumps(data["solution_idea"]),
                key_knowledge=json.dumps(data["key_knowledge"])
            )

        count = self.db.session.query(func.coalesce(func.count(Exercise.exercise_id), 0)).filter(
            Exercise.user_id == session["user_id"], Exercise.branch_id == exercise.branch_id,
            Exercise.compile == 1).scalar()
        if count == 10:
            with self.db.auto_commit():
                stmt = update(ExerciseMans). \
                    where(ExerciseMans.user_id == session["user_id"]). \
                    where(ExerciseMans.branch_id == exercise.branch_id). \
                    values(compile=1)
                self.db.session.execute(stmt)
        return answer_emtry

    def answer_user(self, question: str, answer: str, user_answer: str,
               answer_id: int, exercise_id: int, category_id: int, branch_id: str):
        self.llm = ChatOpenAI(model="gpt-4o-2024-08-06", temperature=0).bind_tools([JsonAnswerTools()], strict=True)

        prompt = ChatPromptTemplate.from_template(self.answer_prompts)
        chain = ({
                     "question": itemgetter("question"),
                     "answer": itemgetter("answer"),
                     "user_answer": itemgetter("user_answer")
                 } | prompt | self.llm | (lambda x: x.tool_calls[0]["args"]) | JsonAnswerTools())

        data = chain.invoke({"question": question, "answer": answer, "user_answer": user_answer})
        exercise = self.db.session.query(ExerciseUser).filter_by(exercise_id=exercise_id,
                                                             user_id=session["user_id"]).one_or_none()
        print("hahahahahahaha")
        print(json.loads(exercise.question_text))
        if exercise is None:
            raise NotFoundException("没有该习题")
        answer_emtry = self.db.session.query(StudentUserAnswer).filter_by(answer_id=answer_id,
                                                                      user_id=session["user_id"]).one_or_none()

        self.update(
            exercise,
            branch_id=exercise.branch_id,
            compile=1
        )
        if answer_emtry is None:
            answer_emtry = self.create(
                StudentUserAnswer,
                score=exercise.score,
                is_correct=True if data["is_correct"] == "1" else False,
                user_answer=json.dumps(user_answer),
                category_id=category_id,
                answer_result=exercise.answer_result,
                branch_id=branch_id,
                exercises_text=json.loads(exercise.question_text),
                detailed_proof=json.dumps(data["detailed_proof"]),
                solution_idea=json.dumps(data["solution_idea"]),
                key_knowledge=json.dumps(data["key_knowledge"]),
                exercise_id=exercise_id,
                user_id=session["user_id"]
            )
        else:
            self.update(
                answer_emtry,
                user_id=session["user_id"],
                answer_id=answer_id,
                is_correct=True if data["is_correct"] == "1" else False,
                user_answer=json.dumps(user_answer),
                answer_result=exercise.answer_result,
                detailed_proof=json.dumps(data["detailed_proof"]),
                solution_idea=json.dumps(data["solution_idea"]),
                key_knowledge=json.dumps(data["key_knowledge"])
            )

        count = self.db.session.query(func.coalesce(func.count(ExerciseUser.exercise_id), 0)).filter(
            ExerciseUser.user_id == session["user_id"], ExerciseUser.branch_id == exercise.branch_id,
            ExerciseUser.compile == 1).scalar()
        if count == 5:
            with self.db.auto_commit():
                stmt = update(ExerciseUserMans). \
                    where(ExerciseUserMans.user_id == session["user_id"]). \
                    where(ExerciseUserMans.branch_id == exercise.branch_id). \
                    values(compile=1)
                self.db.session.execute(stmt)
        return answer_emtry

    # 计算一周内周一到周五每一天完成的类目题目数的百分比
    from datetime import datetime, timedelta
    from sqlalchemy.sql import func

    from datetime import datetime, timedelta
    from sqlalchemy.sql import func

    from datetime import datetime, timedelta
    from sqlalchemy.sql import func

    def calculate_weekly_category_percentages(self):
        today = datetime.now()
        weekday_today = today.weekday()
        name:None
        # 定义本周的工作日范围（周一至周五）
        start_date = today - timedelta(days=weekday_today)  # 周一零点
        start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_date = start_date + timedelta(days=7)  # 周五的午夜

        # 查询工作日内每个类目完成的题目数
        weekly_category_counts = self.db.session.query(
            StudentAnswer.category_id,
            func.count(StudentAnswer.answer_id).label('count'),
            func.date(StudentAnswer.created_at).label('date')
        ).filter(
            StudentAnswer.user_id == session["user_id"],
            StudentAnswer.created_at >= start_date,
            StudentAnswer.created_at <= end_date
        ).group_by(StudentAnswer.category_id, func.date(StudentAnswer.created_at)).all()
        print(weekly_category_counts)
        # 获取每个类目的总题目数
        total_questions_per_category = self.get_total_questions_per_category()

        # 初始化工作日数据
        weekday_data = {
            "Monday": {"categories": {}, "total_count": 0},
            "Tuesday": {"categories": {}, "total_count": 0},
            "Wednesday": {"categories": {}, "total_count": 0},
            "Thursday": {"categories": {}, "total_count": 0},
            "Friday": {"categories": {}, "total_count": 0},
            "Saturday": {"categories": {}, "total_count": 0},
            "Sunday": {"categories": {}, "total_count": 0}
        }

        # 计算百分比并按周几分类
        for category_id, count, date in weekly_category_counts:
            total_questions = total_questions_per_category.get(category_id, 0)
            percentage = (count / total_questions) * 100 if total_questions > 0 else 0
            weekday = datetime.strptime(str(date), "%Y-%m-%d").strftime("%A")  # 获取周几
            print(weekday)
            if weekday in weekday_data:
                category_id_str = str(category_id)  # 转换 category_id 为字符串
                weekday_data[weekday]["categories"][category_id_str] = {
                    "Count": count,
                    "Percentage": f"{percentage:.2f}%"
                }
                weekday_data[weekday]["total_count"] += count

        # 准备结果输出，适合图表显示

        result = {
            "categories": [item.name for item in  self.db.session.query(Category).filter(Category.id.in_(total_questions_per_category.keys()))],  # 转换所有类目为字符串
            "data": []
        }

        # 将每个工作日的类目百分比数据提取出来
        for weekday, data in weekday_data.items():
            weekday_result = {"Weekday": weekday, "TotalCount": data["total_count"]}
            # 提取每个类目在当前工作日的百分比
            for category_id, category_data in data["categories"].items():
                weekday_result[self.db.session.query(Category).filter(Category.id==category_id).one_or_none().name] = category_data["Percentage"]

            result["data"].append(weekday_result)

        return result




    def get_total_questions_per_category(self):
        # 使用 SQLAlchemy 的查询功能来获取每个类目的总题目数
        query = self.db.session.query(
            Exercise.category_id,
            func.count(Exercise.exercise_id).label('total_questions')
        ).group_by(Exercise.category_id).all()

        # 将查询结果转换为一个字典，键是类目 id，值是总题目数
        total_questions_per_category = {row.category_id: row.total_questions for row in query}

        # 如果需要，也可以从 categorys 表中获取类目名称，但这在当前方法中不是必需的
        # categories = self.db.session.query(Category).all()
        # category_name_dict = {category.id: category.name for category in categories}

        return total_questions_per_category





